package com.imooc.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description Nio服务器端
 * @Author
 * @Date 2019/8/27 21:59
 **/
public class NioServer {
    /**
     * 启动
     */
    public void start() throws IOException {
        /**
         * 1.创建Selector
         */
        Selector selector = Selector.open();
        /**
         * 2.通过ServerSocketChannel创建channel通道
         */
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        /**
         * 3.为channel通道绑定监听端口
         */
        serverSocketChannel.bind(new InetSocketAddress(8000));
        /**
         * 4.设置channel为非阻塞模式
         */
        serverSocketChannel.configureBlocking(false);
        /**
         * 5.将channel注册到selector上,监听连接事件
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功!");
        /**
         * 6.循环等待新接入的连接
         */
        for (;;){
            /**
             * todo 获取可用的channel数量
             */
            int readyChannels = selector.select();
            /**
             *  todo 为什么要这么做
             */
            if(readyChannels==0){
                continue;
            }
            /**
             * 获取可用channel的集合
             */
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                /**
                 * 移除Set中的当前 selectionKey  重点
                 *
                 * selector 检测到 SelectionKey 就会放到selector.selectedKeys() 中，所以处理完要移除
                 */
                iterator.remove();
                /**
                 * 7.根据就绪状态，处理具体逻辑
                 *
                 */
                /**
                 * 如果是 接入事件
                 */
                if(selectionKey.isAcceptable()){
                    acceptHandler(serverSocketChannel,selector);
                }
                /**
                 * 如果是 可读事件
                 */
                if(selectionKey.isReadable()){
                    readHandler(selectionKey,selector);
                }
            }

        }

    }

    /**
     * 接入事件处理器
     */
    public void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {
        /**
         * 如果是接入事件，创建socketChannel
         */
        SocketChannel socketChannel = serverSocketChannel.accept();
        /**
         * 将socketChannel设置为非阻塞模式
         */
        socketChannel.configureBlocking(false);
        /**
         * 将channel注册到Selector上，监听可读事件
         */
        socketChannel.register(selector,SelectionKey.OP_READ);
        /**
         * 回复客户端提示信息
         */
        socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室其他人都是不是朋友关系,请注意你的隐私信息！"));
    }

    /**
     * 可读事件处理器
     */
    public void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
        /**
         * 从selectionKey中获取到已经就绪的channel
         */
        SocketChannel socketChannel= (SocketChannel) selectionKey.channel();
        /**
         * 创建buffer
         */
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        /**
         * 循环读取客户端请求信息
         */
        String request="";
        while (socketChannel.read(byteBuffer)>0){
            /**
             * 切换为模式
             */
            byteBuffer.flip();

            /**
             * 读取buffer中的内容
             */
            request+=Charset.forName("UTF-8").decode(byteBuffer);
        }
        /**
         * 将channel再次注册到Selecter上，监听他的可读事件
         */
        socketChannel.register(selector,SelectionKey.OP_READ);
        /**
         * 将客户端信息发送的请求信息广播到其他客户端
         */
        if(request.length()>0){
            //广播给其他客户端
            broadCast(selector,socketChannel,request);
        }
    }

    /**
     * 广播给其他客户端
     */
    private void broadCast(Selector selector,SocketChannel sourceChannel,String request){
        /**
         * 获取到所有已接入的客户端channel
         */
        Set<SelectionKey> selectionKeySet = selector.keys();
        selectionKeySet.forEach(selectionKey -> {
            SelectableChannel targetChannel = selectionKey.channel();
            //剔除发送消息的客户端
            if(targetChannel instanceof SocketChannel&&targetChannel!=sourceChannel){
                try {
                    ((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(request));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        /**
         * 循环向所有客户端发送消息
         */
    }

    public static void main(String[] args) throws IOException {
        NioServer nioServer = new NioServer();
        nioServer.start();
    }
}
